﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace System.Web.Mvc.JQueryValidation
{
	[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
	public class ValidateFormAttribute : ActionFilterAttribute
	{
		private static Dictionary<string, ValidateFormAttribute[]> attrCache = new Dictionary<string, ValidateFormAttribute[]>();

		public string FormID
		{
			get;
			private set;
		}

		public bool ValidateOnExecute
		{
			get;
			private set;
		}

		private ValidatorAttribute[] validators;

		public ValidatorAttribute[] Validators
		{
			get
			{
				return validators;
			}
		}

		public ValidateFormAttribute(string formID)
			: this(formID, true)
		{
		}

		public ValidateFormAttribute(string formID, bool validateOnExecute)
		{
			FormID = formID;
			ValidateOnExecute = validateOnExecute;
		}

		public void SetValidators(ValidatorAttribute[] validators)
		{
			this.validators = validators;
		}

		public override void OnActionExecuting(ActionExecutingContext filterContext)
		{
			string actionParameters = null;
			filterContext.ActionDescriptor.GetParameters().ForEach(p => actionParameters += p.ParameterName);
			var key = filterContext.ActionDescriptor.ActionName
				+ filterContext.ActionDescriptor.ControllerDescriptor.ControllerName
				+ actionParameters;
			ValidateFormAttribute[] validateFormAttributes = null;

			if (attrCache.ContainsKey(key))
			{
				validateFormAttributes = attrCache[key];
			}
			else
			{
				validateFormAttributes = (ValidateFormAttribute[])filterContext.ActionDescriptor.GetCustomAttributes(typeof(ValidateFormAttribute), false);

				if (validateFormAttributes.Length > 0)
				{
					var validators = (ValidatorAttribute[])filterContext.ActionDescriptor.GetCustomAttributes(typeof(ValidatorAttribute), true);

					if (validateFormAttributes.Length == 1)
					{
						validators.ForEach(v => v.FormID = validateFormAttributes[0].FormID);
					}

					validateFormAttributes.ForEach(v => v.SetValidators(validators.Where(vt => vt.FormID == v.FormID).ToArray()));

				}

				attrCache.Add(key, validateFormAttributes);
			}

			if (validateFormAttributes != null)
			{
				validateFormAttributes.ForEach(v => ProcessFormValidation(v, filterContext));
				filterContext.Controller.ViewData["JQueryValidators"] = validateFormAttributes;
			}
		}

		private void ProcessFormValidation(ValidateFormAttribute validateForm, ActionExecutingContext filterContext)
		{
			if (validateForm.ValidateOnExecute)
			{
				foreach (var validator in validateForm.Validators)
				{
					if (!validator.Validate(filterContext.HttpContext.Request[validator.FieldName]))
					{
						filterContext.Controller.ViewData.ModelState.AddModelError(validator.FieldName, validator.ServerErrorMessage);
					}
				}
			}
		}
	}
}